import os.path
import time

from pywinauto import Desktop
from selenium.webdriver import ActionChains
from selenium.webdriver.common.keys import Keys

from config.global_settings import ENTRY_DOWNLOAD_PATH, OFFICIAL_URL, SUPPORT_FJ
from data_util.file_download import download
from data_util.my_log import logger
from data_util.rsa_pwd import RSACipher
from zjbj.exception_util.web_exception import EntryProcessError, TimeoutImageUploadError
from zjbj.web_util.web_robot import WebRobot
from config.global_settings import DEFAULT_WAIT


class EntryRobot(WebRobot):
    def __init__(self, url, download_path, body):
        """
        初始化
        :param url: 八局url
        :param download_path: 附件/发票下载路径
        :param body: 填报信息
        """
        super().__init__(url, download_path)
        self.download_path = download_path
        self.url = url
        self.body = body
        self._back_bxlx = 'jzd' if self.body.get('bxlx') == '对外成本费用付款申请' else 'bxd'

    # 八局单据填报流程入口
    def run(self):
        # 下载附件和发票
        self.download_file()

        # 登录
        self.login()

        # 判断是否是打回的单据重新提交(单据编号是八局一体化系统的编号，如果该字段有值则说明是被打回的单据二次提交)
        djbh = self.body.get('djbh')
        if djbh:
            print('二次提交')
        else:
            # 切换组织机构
            self.check_department()

            # 选择填报单据类型
            self.check_to_entry()

    # 重写父类方法
    def find_element(self, xpath, timeout=DEFAULT_WAIT):
        try:
            ele = super().find_element(xpath, DEFAULT_WAIT)
            return ele
        except:
            return None

    # 八局单据填报流程具体执行方法⬇
    # 登录
    def login(self):
        username = self.body.get('userName')
        password = self.body.get('password')
        # 解密用户名和密码
        try:
            cipher = RSACipher()
            username = cipher.decrypt_with_private_key(username)
            password = cipher.decrypt_with_private_key(password)
        except:
            self.exit({
                "code": False,
                "is_notice": True,
                "result": "[RPA ERROR]: 登录出错，用户名或密码解密失败！"
            }, user_pwd_check="校验失败")

        try:
            self.driver.get(self.url)
            # implicitly_wait(2)属于隐式等待，2秒钟内只要找到了元素就开始执行，2秒钟后未找到，就超时；
            self.driver.implicitly_wait(2)
            self.fill_input('//input[@id="username"]', username)
            self.fill_input('//input[@id="password"]', password)
            self.click_element('//div[@class="submit"]', mode='internal')
        except:
            self.exit({
                "code": False,
                "is_notice": False,
                "result": "[RPA ERROR]: 网络延迟，登录页无法打开！"
            }, user_pwd_check='未校验')
        if self.find_element('//span[contains(text(), "用户名或密码错误")]', timeout=2):
            self.exit({
                "code": False,
                "is_notice": True,
                "result": "登录出错，用户名或密码错误！"
            }, user_pwd_check='校验失败')
        logger.info('login success ...')
        # 登录成功后一体化系统会弹出通知公告框，点击确定让公告框消失
        try:
            self.click_element('//button/span[text()="确 定"]', mode='internal', timeout=20)
        except:
            ...
        time.sleep(1)

    # 下载附件和发票
    def download_file(self):

        # 下载交通类发票(差旅报销单) todo
        # 下载住宿费发票(差旅报销单) todo
        # 下载其他类发票(差旅报销单) todo
        # 下载附件
        fj_list = self.body.get('fj')
        if fj_list:
            for fj_url in fj_list:
                fj_name = fj_url.split("/")[-1]
                dir_path = os.path.join(self.download_path, 'fj')
                if not os.path.exists(dir_path):
                    os.mkdir(dir_path)
                fj_path = os.path.join(dir_path, fj_name)
                flag = download(fj_url, fj_path)
                if not flag:
                    self.exit({
                        "code": False,
                        "is_notice": False,
                        "result": f"[RPA ERROR]: 附件下载错误！"
                    })

        # 下载费用事项发票
        costMatterList = self.body.get('costMatterList')
        if costMatterList:
            for dic in costMatterList:
                fp_url = dic.get('fpxx')
                fphm = dic.get('fphm')
                if not fp_url:
                    continue
                fp_name = fp_url.split('/')[-1]
                dir_path = os.path.join(self.download_path, 'fp')
                if not os.path.exists(dir_path):
                    os.mkdir(dir_path)
                fp_path = os.path.join(dir_path, fp_name)
                flag = download(fp_url, fp_path)
                if not flag:
                    self.exit({
                        "code": False,
                        "is_notice": False,
                        "result": f"[RPA ERROR]: 发票{fphm}下载错误！"
                    })

                # 发票-附件类
                fj_lis = dic.get('fj')
                if not fj_lis:
                    continue
                for fj_url in fj_lis:
                    fj_name = fj_url.split('/')[-1]
                    dir_path = os.path.join(self.download_path, 'fp')
                    if not os.path.exists(dir_path):
                        os.mkdir(dir_path)
                    fj_path = os.path.join(dir_path, fj_name)
                    flag = download(fj_url, fj_path)
                    if not flag:
                        self.exit({
                            "code": False,
                            "is_notice": False,
                            "result": f"[RPA ERROR]: 发票[{fphm}]-附件下载错误！"
                        })

    # 返回填报结果
    def exit(self, err_msg, user_pwd_check="校验成功"):
        err_msg['user_pwd_check'] = user_pwd_check
        err_msg['bxdId'] = self.body.get('bxdId')
        err_msg['creater'] = self.body.get('creater')
        err_msg['djlx'] = self._back_bxlx
        self.driver.close()
        raise EntryProcessError('填报数据无法完成录单操作！')

    # 切换组织机构
    def check_department(self):
        """
        切换部门
        :return:
        """
        zzjg = self.body.get('zzjg')
        ywbm = self.body.get('ywbm')
        xmmc = self.body.get('xmmc')

        # 点击按钮弹出组织机构切换框
        self.click_element('//img[contains(@src,"qhzz")]', mode='internal')

        # 点击按钮弹出组织机构切换框
        # 点击？按钮弹出组织机构选择框
        self.click_element('//input[@id="DataSetFieldComboBox1-input"]/following-sibling::div', mode='internal')
        self.input_enter_choice(zzjg, choice_order=0, choice_type='zzjg', located="组织机构")

        # 点击按钮弹出项目名称切换框
        # 点击？按钮弹出项目名称选择框
        self.click_element('//input[@id="DataSetFieldComboBox3-input"]/following-sibling::div', mode='internal')
        self.input_enter_choice(xmmc, choice_order=0, choice_type='xmmc', located="项目名称")

        # 点击按钮弹出部门名称切换框
        # 点击？按钮弹出部门选择框
        self.click_element('//input[@id="DataSetFieldComboBox2-input"]/following-sibling::div', mode='internal')
        self.input_enter_choice(ywbm, choice_order=0, choice_type='ywbm', located="部门名称")

        self.click_element('//div[contains(text(),"确定")]')
        logger.info('切换组织机构完成!')

    # 处理？选择框
    def input_enter_choice(self, content, choice_order=0, choice_type="bzzd", located=""):
        """
        bzzd:帮助字典
        :param content: 输入框需要输入的内容
        :param choice_order:
        :param choice_type:
        :param located:
        :return:
        """
        content = content.strip()
        if choice_type in ["zzjg", "xmmc", "ywbm"]:
            xpath = '//input[@id="undefined-input" and @placeholder="请输入查询关键字"]'
        # 输入内容
        self.fill_input(xpath, content)
        time.sleep(1)
        # 输入内容后点击回车，执行查询
        self.find_element(xpath).send_keys(Keys.ENTER)
        if choice_type == "zzjg":
            choice_xpath = "//div[contains(text(),'{}')]".format(content)
        elif choice_type == "xmmc":
            choice_xpath = "//div[contains(text(),'{}')]".format(content)
        elif choice_type == "ywbm":
            choice_xpath = "//div[contains(text(),'{}')]".format(content)
        time.sleep(1)
        try:
            # 双击选择元素
            ActionChains(self.driver).double_click(self.find_elements(choice_xpath)[choice_order]).perform()
        except:
            # todo 下面result中的located和content没显示具体的值
            self.exit({
                'code': False,
                "is_notice": False,
                'result': f'[RPA ERROR]: {located}: 没有找到[{content}]控件或网络延迟严重!'
            })
        time.sleep(1)

    # 选择填报单据类型
    def check_to_entry(self):
        """
        选择填报单据类型
        :return:
        """
        _type = self.body.get("bxlx")
        title = '应付及付款' if _type == '对外成本费用付款申请' else '员工薪酬及费用'

        self.click_element('//div[@class="hamburger-container"]', mode='internal')
        self.click_element('//ul/li//span[text()="报账系统"]', mode='internal')
        self.click_element(f"//li/span[contains(text(),'{title}')]", mode='internal')
        time.sleep(1)
        self.click_element("//li/div[@class='react-contextmenu-wrapper']/span[contains(text(),'{}')]".format(_type),
                           mode='internal')
        time.sleep(1)

        if _type == '差旅费报销':
            logger.info('当前进入 -- 差旅费报销')
        elif _type == '通用报销单':
            logger.info('当前进入 -- 通用报销单')
            self.general_expense_entry()
        elif _type == "对外成本费用付款申请":
            logger.info('当前进入 -- 对外成本费用付款申请')

    # 通用报销单填报
    def general_expense_entry(self):
        """
       通用单据填报
       :return:
       """
        self.upload_image()

        self._upload_file()



    def upload_image(self):
        """
        上传发票
        :return:
        """
        image_type = {
            '发票标签': [],
            '非发票标签': []
        }
        # 费用承担事项
        costMatterList = self.body.get('costMatterList')
        for cml in costMatterList:
            fpxx = cml.get('fpxx')
            fphm = cml.get('fphm')
            fpbq = cml.get('fpbq')
            if not fpxx:
                continue
            fp_name = fpxx.split('/')[-1]
            fp_path = os.path.join(self.download_path, 'fp', fp_name)
            if fpbq:
                image_type['发票标签'].append({'path': fp_path, 'msg': f'[{fphm}]', 'fphm': fphm})
            else:
                image_type['非发票标签'].append({'path': fp_path, 'msg': f'[{fphm}]'})

            fjxx = cml.get('fj')
            if not fjxx:
                continue
            for fj_url in fjxx:
                fj_name = fj_url.split('/')[-1]
                fj_path = os.path.join(self.download_path, 'fp', fj_name)
                image_type['非发票标签'].append({'path': fj_path, 'msg': '附件'})

        # 附件不支持的格式上传到非发票标签
        fj_list = self.body.get('fj')
        for fj_url in fj_list:
            fj_name = fj_url.split('/')[-1]
            fj_path = os.path.join(self.download_path, 'fj', fj_name)
            fj_type = os.path.splitext(fj_name)[-1]
            if fj_type not in SUPPORT_FJ:
                image_type['非发票标签'].append({'path': fj_path, 'msg': '附件'})

        # 既没有发票也没有非发票
        if not image_type['发票标签'] and not image_type['非发票标签']:
            return

        self.click_element('//span[text()="影像"]', mode='internal')
        for _type, image_path_lis in image_type.items():
            if not image_path_lis:
                continue
            for image_dic in image_path_lis:
                image_path = image_dic.get("path")
                msg = image_dic.get("msg")
                logger.info(f'{_type}: {msg} - {image_path}')
                result = self.try_upload(_type, image_path)
                logger.info(f'{msg}: {result}')
                if _type == "发票标签":
                    # 增值税发票未查验标签
                    uncheck_flag = self.find_element('//img[contains(@src,"uncheck")]', timeout=1)

                    fphm = image_dic.get('fphm')
                    if uncheck_flag and '查验成功，发票不一致' in result:
                        logger.info('查验成功，发票不一致, 开始修改发票信息')
                        self.click_element('//div[text()="{}"]'.format('退出'))
                        result = self.modify_fp(fphm, image_path)
                        if "失败" in result or '出现问题' in result:
                            self.exit({
                                "code": False,
                                "is_notice": True,
                                "result": f'修改发票信息后，影像-{msg}识别查验失败',
                                "email_result": result
                            })
                    else:
                        if uncheck_flag or "失败" in result or '出现问题' in result:
                            logger.info(f'增值税发票开始进行一次重试上传操作...')
                            self.click_element('//div[text()="{}"]'.format('退出'))
                            self.find_elements('//div[@title="删除"]')[0].click()
                            result = self.try_upload(_type, image_path)
                            uncheck_flag = self.find_element('//img[contains(@src,"uncheck")]', timeout=1)
                            if uncheck_flag or "失败" in result or '出现问题' in result:
                                self.exit({
                                    "code": False,
                                    "is_notice": True,
                                    "result": f'影像-{msg}识别查验失败',
                                    "email_result": result
                                })
                else:
                    if "失败" in result or '出现问题' in result:
                        if "附件" in msg:
                            res = "存在不能识别的附件，无法上传到非发票标签"
                        else:
                            res = f'影像-{msg}识别查验失败'
                        self.exit({
                            "code": False,
                            "is_notice": True,
                            "result": res,
                            "email_result": result
                        })
                # 查验成功，点击退出
                time.sleep(2)
                self.click_element('//div[text()="{}"]'.format('退出'))

        # 退出
        self.click_element('//*[contains(@id, "VBoxLayout")]/div/div/div/div/div/div[3]/div/div[8]',
                           mode='internal')
        # 校验失败需要点击按钮
        if self.find_element('//div[text()="影像提示 "]', timeout=1):
            self.exit({
                "code": False,
                "is_notice": False,
                "result": "存在不能提交的影像或附件，请检查"
            })
        time.sleep(1)
    def try_upload(self, _type, image_path):
        """
        上传影像
        :param _type:
        :param image_path:
        :return:
        """
        # 点击发票标签/非发票标签
        try:
            self.click_element('//span[text()="{}"]'.format(_type), mode='internal')
        except:
            self.find_elements('//button/span[text()="确 定"]/parent::button')[-1].click()
            self.click_element('//span[text()="{}"]'.format(_type), mode='internal')
        # 点击本地影像
        self.click_element('//*[contains(@id, "VBoxLayout")]/div/div/div/div/div/div[3]/div/div[4]',
                           mode='internal')

        # 输入文件名上传影像
        title = self.get_html_title()
        windows = Desktop(backend='uia')
        win = windows[f'{title} - Google Chrome']
        if win.exists():
            editor = win.child_window(title="文件名(N):", control_type="Edit")
            editor.type_keys(image_path, with_spaces=True)
            time.sleep(0.5)
            editor.type_keys("{ENTER}")
        time.sleep(1)

        if not self.wait_element('//div[text()="{}"]'.format('退出'), times=15):
            raise TimeoutImageUploadError("影像上传超时！")

        # 查看提交影像是否通过查验
        time.sleep(0.5)
        _, image_name = os.path.split(image_path)
        result_element = self.find_element(
            '//td[@title="{}"]/following-sibling::td'.format(image_name))
        result = result_element.get_attribute('title')
        return result

    def get_html_title(self):
        title_xpath = 'html/head/title'
        ele = self.find_element(title_xpath)
        title = ele.get_attribute("textContent")
        return title

    def wait_element(self, element_xpath, times=10):
        """
        等待标签
        :param element_xpath:
        :param times:
        :return:
        """
        retry_out_times = 0
        while retry_out_times < times:
            _exist = self.find_element(element_xpath)
            if not _exist:
                retry_out_times += 1
                if retry_out_times == times:
                    return False
                continue
            else:
                return True

    def _upload_file(self):
        """
        上传附件
        :return:
        """
        time.sleep(1)
        for fj_url in self.body.get('fj'):
            fj_name = fj_url.split('/')[-1]
            dir_path = os.path.join(self.download_path, 'fj')
            fj_path = os.path.join(dir_path, fj_name)
            fj_type = os.path.splitext(fj_name)[-1]
            if fj_type not in SUPPORT_FJ:
                # 不上传附件不支持的类型的文件
                continue
            self.click_element('//span[text()="{}"]'.format('附件'), mode='internal')
            self.click_element('//span[text()="{}"]'.format('上传'), mode='internal')

            title = self.get_html_title()
            windows = Desktop(backend='uia')
            win = windows[f'{title} - Google Chrome']
            if win.exists():
                editor = win.child_window(title="文件名(N):", control_type="Edit")
                editor.type_keys(fj_path, with_spaces=True)
                time.sleep(0.5)
                editor.type_keys("{ENTER}")

            time.sleep(0.5)
            self.click_element('//div[text()="确定"]', mode='internal')
            # self.click_element('//*[contains(@id, "x-auto")]/div[2]/table/tbody/tr/td[3]/div', mode='internal')
            # self.click_element('//*[contains(@id, "x-auto-123-label")]/../div[2]/table/tbody/tr/td[3]/div', mode='internal')
            # self.click_element('//*[contains(@id, "x-auto-126")]/../div[2]/table/tbody/tr/td[3]/div', mode='internal')
            # 0319
            self.click_element('//div[@class="CRUBV1B-a-g"]/div//../div[2]/table/tbody/tr/td[3]/div', mode='internal')

    # 八局单据填报流程具体执行方法⬆
if __name__ == "__main__":
    body = {'bxdId': 1503334099135254529, 'reimbCode': 'BZ-DSBX202203140081', 'creater': 1095,
            'djbh': '',
            'userName': 'a7lgj2QQiJNsbH3Xalw2099vPl1giIs1vfvbPEvW5s4mHxcumSQNXhYqy9a2SLztEBK11i47Swi+4V/sv8ENGbmyZig70aUe9k0XWdtbo35iySAh0JI6ChCZSNDTho39MOyKmU8Xxewez5xL1LihxHsDIRJir0pZ7zeF+BeZaRI\u003d',
            'password': 'nSITC3yhXlaLnMSqkae9v8wF8dNVvdYE0H9HPc/I131nPaGzqE3snMoZDAzX7CkC31RwflQh6lK44drQvmuPfFtiwwhfZNZAY/TqaJi4rL92hnb5GMISyetb1dARGl0CTwgi2RqUwuts6KF2Mm54r7hpZb1g2B8hkUpIXXSJ784\u003d',
            'bxsy': '机关付财务资金部年审邮递费', 'bz': '人民币', 'zzjg': '中建八局科技建设有限公司-本部', 'ywbm': '中建八局科技公司财务资金部',
            'xmmc': '中建八局科技建设有限公司-本部项目', 'bxsx': '0070', 'bxlx': '通用报销单', 'yslb': 'ZX',
            'fj': ['https://bill.cscec8st.com.cn//file/reimbPic/BZ-DSBX202203140081.png'], 'costMatterList': [
            {'sy': '中建八局科技建设有限公司2021年度审计邮递费用', 'xm': '王庆', 'bh': '20189495', 'bxfysx_text': '审计费/其他',
             'bxfysx': '007000700020', 'fyje': 120.000000, 'fj': [
                "https://bzrobotdev.iflysec.com/bzrobot/file/ocr/20220323/73580889/31c89b8930f5452293b6e4c5069f3162.jpeg",
                "https://bzrobotdev.iflysec.com/bzrobot/file/ocr/20220329/73580889/fc3d200ad7934653a3d15d90b490e473.jpeg",
                "https://bzrobotdev.iflysec.com/bzrobot/file/ocr/20220329/73580889/f21c9d9853ff47248cb7223684afc845.jpeg"],
             'fpxx': 'https://bill.cscec8st.com.cn//file/ocr/20220311/1095/b6e12896e5d74a0bb73a63defd79e008/dd5b284c8c554097b2ae8d8a6967b82f.jpeg',
             'fphm': '13211389', 'fpbq': False, 'parentId': '660208'}], 'traffics': [],
            'costAssumeMatter': {'yslb': 'ZX', 'ysxm': '6602130199', 'fygs': '中建八局科技公司财务资金部', 'yssx': ''},
            'skxx': {'20189495': {'fkzy': 'ZZ20099475#八局科技本部#付王庆', 'fkje': '120.00'}},
            'approvers': ['梁宾', '禹顺强', '梁宾', '胡彰来', '梁宾', '胡彰来', '盛金明', '张正洪', '张子俊']}
    if not os.path.exists(ENTRY_DOWNLOAD_PATH):
        os.mkdir(ENTRY_DOWNLOAD_PATH)
    DownLoadPath = os.path.join(ENTRY_DOWNLOAD_PATH, str(body.get('bxdId')))
    if not os.path.exists(DownLoadPath):
        os.mkdir(DownLoadPath)

    robot = EntryRobot(url=OFFICIAL_URL, download_path=DownLoadPath, body=body)
    robot.run()
